package stream;

import lambda.Employee;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 终止操作
 */
public class TestStreamApi3 {

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99, Employee.Status.FREE),
            new Employee("李四", 58, 5555.55, Employee.Status.BUSY),
            new Employee("王五", 26, 3333.33, Employee.Status.VOCATION),
            new Employee("赵六", 36, 6666.66, Employee.Status.BUSY),
            new Employee("田七", 12, 8888.88, Employee.Status.BUSY)
    );

    /**
     * 收集
     * collect——将流转换成其他形式。接收一个Collector接口的实现
     */

    @Test
    public void test10(){
        String joining = employees.stream()
                .map(Employee::getName)
                // 将Name连接到一起了
                .collect(Collectors.joining());
        System.out.println(joining);
    }

    @Test
    public void test9(){
        // 另外一种获取方式
        DoubleSummaryStatistics dss = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(dss.getSum());
        System.out.println(dss.getAverage());
    }

    // 分区
    @Test
    public void test8(){
        Map<Boolean,List<Employee>> map = employees.stream()
                .collect(Collectors.partitioningBy((e) -> e.getSalary()>8000));
        System.out.println(map);
    }

    // 多级分组
    // 方法引用：Lambda 体中调用方法的参数列表和返回值类型，要与函数式接口中抽象方法的函数列表和返回值类型保持一致
    @Test
    public void test7() {
        Map<Employee.Status,Map<String,List<Employee>>> map = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
                    if ((e).getAge() <=35) {
                        return "青年";
                    } else if ((e).getAge() <=50) {
                        return "中年";
                    } else {
                        return "老年";
                    }
                })));
        System.out.println(map);
    }

    // 分组
    // 方法引用：Lambda 体中调用方法的参数列表和返回值类型，要与函数式接口中抽象方法的函数列表和返回值类型保持一致
    @Test
    public void test6() {
        Map<Employee.Status,List<Employee>> map = employees.stream()
        .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
    }

    @Test
    public void test5() {
        // 总数
        Long count = employees.stream()
                .collect(Collectors.counting());
        System.out.println(count);
        // 按salary 获取平均值
        Double avg = employees.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);
        // 获取 salary 总和
        Double sumSalary = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sumSalary);
        // 获取 salary 最大的员工信息
        // 写法1
        Optional<Employee> max1 = employees.stream()
                .collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())));
        Optional<Employee> max2 = employees.stream()
                .max((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()));

        Optional<Employee> max = employees.stream().max(Comparator.comparingDouble(Employee::getSalary));
        System.out.println(max.get());

        // 获取 salary 最小值
        Optional<Double> minSalary = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        System.out.println(minSalary.get());

    }

    @Test
    public void test4() {
        // 将所有员工的名字收集到List中
        List<String> nameList = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        nameList.forEach(System.out::println);

        System.out.println("--------------------");

        // 将所有员工的名字收集到Set集合中
        Set<String> nameSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());
        nameSet.forEach(System.out::println);

        System.out.println("--------------------");

        HashSet<String> nameHashSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        nameHashSet.forEach(System.out::println);
    }


    /**
     * 规约
     * reduce(T identity, BinaryOperator<T> accumulator) —— 可以将流中元素反复结合起来，得到一个值
     */

    @Test
    public void test3() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream()
                .reduce(0, (x,y) -> x + y);
        System.out.println(sum);

        System.out.println("-----------------------");

        /**
         * 需求：计算所有员工的工资总和
         * 1. 首先需要通过流，获取所有工资（map映射获取）
         * 2. 再通过规约进行计算
         */
        Optional<Double> sumSalary = employees.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(sumSalary.get());
    }

    /**
     * 查找与匹配
     * allMatch —— 检查是否匹配所有元素
     * anyMatch —— 检查是否匹配至少一个元素
     * noneMatch —— 检查是否没有匹配所有元素
     * findFirst —— 返回第一个元素
     * findAny —— 返回当前流中的任意元素
     * count —— 返回流中元素的总个数
     * max —— 返回流中的最大值
     * min —— 返回流中的最小值
     */

    // 匹配
    @Test
    public void test2() {
        Long count = employees.stream()
                .count(); // 返回流中元素的总个数(employees集合中元素的个数)
        System.out.println(count);

        Optional<Employee> op1 = employees.stream()
//                .max((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())); // 按Salary找出一个最大值
                .max(Comparator.comparingDouble(Employee::getSalary));
        System.out.println(op1.get());

        // 获取Salary 最小的具体值
        Optional<Double> op2 = employees.stream()
                .map(Employee::getSalary) // 接收lambda，将元素转换为其他形式或者提取信息。接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新元素。
                .min(Double::compareTo);
        System.out.println(op2.get());
    }

    // 查找
    @Test
    public void test1() {
        boolean b1 = employees.stream()
                .allMatch((employee -> employee.getStatus().equals(Employee.Status.BUSY))); // allMatch —— 检查是否匹配所有元素
        System.out.println(b1);
        boolean b2 = employees.stream()
                .anyMatch((employee -> employee.getStatus().equals(Employee.Status.BUSY))); // anyMatch —— 检查是否至少匹配一个元素
        System.out.println(b2);

        boolean b3 = employees.stream()
                .noneMatch((employee -> employee.getStatus().equals(Employee.Status.BUSY))); // noneMatch —— 检查是否没有匹配所有元素
        System.out.println(b3); // 不是没有匹配所有元素

        // Optional: 容器类，将可能为空的对象放在此容器内。在一定程度上避免为空了
        Optional<Employee> first = employees.stream()
                .sorted(Comparator.comparingDouble(Employee::getSalary)) // 按照Salary正序排序
                .findFirst(); //获取排序后的第一个对象
        System.out.println(first.get());

        // stream() 串行流
        // parallelStream() 并行流
        Optional<Employee> anyFree = employees.parallelStream()
                .filter((e) -> e.getStatus().equals(Employee.Status.FREE)) // 筛选是FREE状态的元素
                .findAny(); // 返回是 FREE 状态的任意一个对象
        System.out.println(anyFree.get());
    }
}
